#include "StdAfx.h"
#include "Network.h"

#include "GSHTTPGateway.h"

CGSHTTPGateway::CGSHTTPGateway():m_lastId(0)
{
	ghttpStartup();
	SCOPED_GLOBAL_LOCK;
	m_updateTimer = TIMER.AddTimer( g_time + 0.05f, CGSHTTPGateway::TimerCallback, this );
}

CGSHTTPGateway::~CGSHTTPGateway()
{
	CleanUp();
}

void CGSHTTPGateway::CleanUp()
{
	for(TJobMap::iterator it = m_requests.begin(), eit = m_requests.end(); it != eit; ++it)
	{
		ghttpCancelRequest(it->second.request);
	}
	m_requests.clear();
	m_filedata.clear();

	for(std::map<int, IDownloadStream*>::iterator it = m_callbacks.begin(), eit = m_callbacks.end(); it!= eit; ++it)
	{
		it->second->Complete(false);
	}
	ghttpCleanup();
}

int CGSHTTPGateway::GetURL(const char* url, IDownloadStream* stream)
{
	int id = m_lastId++;
	m_callbacks.insert(std::make_pair(id, stream));
	SCOPED_GLOBAL_LOCK;
	FROM_GAME(&CGSHTTPGateway::NC_GetURL, this, id, string(url));
	return id;
}

int CGSHTTPGateway::PostURL(const char* url, const char* params, IDownloadStream* stream)
{
	int id = m_lastId++;
	m_callbacks.insert(std::make_pair(id, stream));
	SCOPED_GLOBAL_LOCK;
	FROM_GAME(&CGSHTTPGateway::NC_PostURL, this, id, string(url), string(params), (SUploadFile*)0);
	return 0;
}

int CGSHTTPGateway::PostFileToURL(const char* url, const char* params, const char* name, const uint8* data, uint32 size, const char* mime, IDownloadStream* stream)
{
	int id = m_lastId++;
	m_callbacks.insert(std::make_pair(id, stream));
	TFileMap::iterator it = m_filedata.insert(std::make_pair(id, SUploadFile())).first;
	SUploadFile& file = it->second;
	file.name = name;
	file.mime = mime;
	file.data.insert(file.data.end(), data, data + size);
	SCOPED_GLOBAL_LOCK;
	FROM_GAME(&CGSHTTPGateway::NC_PostURL, this, id, string(url), string(params), &(it->second));
	return 0;
}

void CGSHTTPGateway::CancelRequest(int id)
{
	SCOPED_GLOBAL_LOCK;
	FROM_GAME(&CGSHTTPGateway::NC_CancelRequest, this, id);
	return;
}

void CGSHTTPGateway::NC_GetURL(int id, string url)
{
	TJobMap::iterator it = m_requests.insert(std::make_pair(id, SHTTPJob())).first;
	SHTTPJob &job = it->second;
	job.id = id;
	job.result = GHTTPRequestCancelled;
	job.owner = this;
	job.request = ghttpStream	(url.c_str(), GHTTPFalse, ProgressCallback, CompletedCallback, &(it->second));
	if(job.request < 0)
	{
		SCOPED_GLOBAL_LOCK;
		TO_GAME(&CGSHTTPGateway::GC_Completed, this, id, (uint8*)0, (uint32)0, false);
		m_requests.erase(it);
	}
}

void FillPostParams(GHTTPPost post, const char* params)
{
	const char* p = params;
	string name;
	string value;
	bool	param = true;
	do
	{
		switch(*p)
		{
		case 0:
		case '&':
		case '?':
			{
				if(!name.empty())
				{
					ghttpPostAddString(post, name.c_str(), value.c_str());
				}
				name.resize(0);
				value.resize(0);
				param = true;
			}
			break;
		case '=':
			param = false;
			break;
		default:
			if(param)
				name.push_back(*p);
			else
				value.push_back(*p);
		}
		if(*p)
			p++;
		else
			break;
	}while(1);
}


void CGSHTTPGateway::NC_PostURL(int id, string url, string params, SUploadFile* pFile)
{
	TJobMap::iterator it = m_requests.insert(std::make_pair(id, SHTTPJob())).first;
	SHTTPJob &job = it->second;
	job.id = id;
	job.result = GHTTPRequestCancelled;
	job.owner = this;

	GHTTPPost post = ghttpNewPost();
	FillPostParams(post, params.c_str());

	if(pFile)
		ghttpPostAddFileFromMemory(post, pFile->name.c_str(), (const char*)&(pFile->data[0]), pFile->data.size(), pFile->name.c_str(), pFile->mime.c_str());

	job.request = ghttpGetEx(url.c_str(), "", 0, 0, post, GHTTPFalse, GHTTPFalse, ProgressCallback, CompletedCallback, &(it->second));

	if(job.request < 0)
	{
		SCOPED_GLOBAL_LOCK;
		TO_GAME(&CGSHTTPGateway::GC_Completed, this, id, (uint8*)0, (uint32)0, false);
		m_requests.erase(it);
	}
}

void CGSHTTPGateway::NC_CancelRequest(int id)
{
	TJobMap::iterator it = m_requests.find(id);
	if(it != m_requests.end())
	{
		SCOPED_GLOBAL_LOCK;
		ghttpCancelRequest(it->second.request);
		TO_GAME(&CGSHTTPGateway::GC_Completed, this, id, (uint8*)0, (uint32)0, it->second.result == GHTTPSuccess);
		m_requests.erase(it);
	}
}

void CGSHTTPGateway::GC_Completed(int id, uint8* data, uint32 size, bool success)
{
	std::map<int, IDownloadStream*>::iterator it = m_callbacks.find(id);
	if(it != m_callbacks.end())
	{
		if(data && size)
			it->second->GotData(data, size);
		it->second->Complete(success);
		m_callbacks.erase(it);
	}
	TFileMap::iterator f_it = m_filedata.find(id);
	if(f_it != m_filedata.end())
	{
		m_filedata.erase(f_it);		
	}
}

GHTTPBool CGSHTTPGateway::CompletedCallback( GHTTPRequest request, GHTTPResult result, char * buffer, GHTTPByteCount bufferLen, void * param )
{
	SHTTPJob* job = static_cast<SHTTPJob*>(param);
	
	NET_ASSERT(job->data.size() == bufferLen);

	job->result = result;
	SCOPED_GLOBAL_LOCK;

	TO_GAME(&CGSHTTPGateway::GC_Completed, job->owner, job->id, &job->data[0], (uint32)job->data.size(), result == GHTTPSuccess);
			
	return GHTTPTrue;
}

void CGSHTTPGateway::ProgressCallback( GHTTPRequest request, GHTTPState state, const char * buffer, GHTTPByteCount bufferLen, GHTTPByteCount bytesReceived, GHTTPByteCount totalSize, void * param )
{
	SHTTPJob* job = static_cast<SHTTPJob*>(param);
	if(bufferLen > 0)
	{
		job->data.insert(job->data.end(), (uint8*)buffer, (uint8*)buffer + bufferLen);

		NET_ASSERT(job->data.size() == bytesReceived);
	}
}


void CGSHTTPGateway::TimerCallback( NetTimerId id, void * pUser, CTimeValue tm )
{
	CGSHTTPGateway *pGateway = static_cast<CGSHTTPGateway*>(pUser);
	if(id != pGateway->m_updateTimer)
		return;
	ghttpThink();
	pGateway->m_updateTimer = TIMER.AddTimer( g_time + 0.05f, TimerCallback, pGateway );
}